#include "serveur.hpp" // Variables globales et fonctions

using namespace std;

/*
	Serveur gerant la grille
*/
	
int main(int argc, char **argv)
{
	// Gestion des erreurs
	string err1 = "Echec : Pas assez ou trop d'arguments ";
	string err2 = "Echec : Creation du serveur impossible ";
	string err3 = "Echec : Creation de la file impossible ";
	string err4 = "Echec : Connexion du nouveau client impossible ";
	
	if (argc < 2 || argc > 5)
	{
		cout << err1 << endl;
		// Afficher l'aide
		usage(stdout);
		return 0;
	}
	
	// Recuperation des arguments
	unsigned short port = (short)(atoi(argv[1]));
	struct	sockaddr_in socketCV;
	int nbClientsMax = 10;
	nbMouvementsMax = 3;
	historiqueGrille = 5; 
	
	// Nombre de mouvements que peut faire le client dans la grille avant de perdre le controle	
	if(argv[2] != NULL)
		nbMouvementsMax = atoi(argv[2]);
		
	// Si le nombre max de clients est renseigne
	if(argv[3] != NULL && argc >= 4)
		nbClientsMax = atoi(argv[3]);
	
	// Nombre de mouvements avant reinitialisation de la grille
	if(argv[4] != NULL && argc >= 5)
		historiqueGrille = atoi(argv[4]);
	
	// Variables serveur
	Sock socketServeur(SOCK_STREAM, port, 0);
	int descripteurServeur;
	int longueurFile = 5;
	controle = -1;
	compteurGrille = historiqueGrille;
	
	socklen_t longueurSocketCV;
	
	cout << "Mouvements maximum : " << nbMouvementsMax;
	cout << ", nombre de clients maximum connectes en meme temps : " << nbClientsMax << endl;
	cout << "Mouvements avant reinitialisation : " << compteurGrille << endl;
	
	// Creation du serveur
	if (socketServeur.good())
	{
		descripteurServeur = socketServeur.getsDesc();
	}
	else
	{	
		perror(err2.c_str());
		return 0;
	}
	
	// Creation de la file d'attente 
	int r = listen(descripteurServeur,longueurFile);
	if (r < 0)
	{
		perror(err3.c_str());
		return 0;
	}
	
	cout << "Serveur en attente sur le port " << port << endl;
	
	// Threads ecoutant le clavier pour terminer proprement le serveur
	pthread_t tc;
	pthread_create(&tc, NULL, threadCommandes, &descripteurServeur);
	
	// Tableau des threads clients
	pthread_t tabThreadsEnvoi[nbClientsMax];
	pthread_t tabThreadsReception[nbClientsMax];
	
	// Mise en attente du serveur
	while(1)
	{
		longueurSocketCV = sizeof(struct sockaddr_in);
		int descripteurCV = accept(descripteurServeur, (struct sockaddr*) &socketCV, &longueurSocketCV);
		if (descripteurCV < 0)
		{
			perror(err4.c_str());
			return 0;
		}
		
		// Si il n'y a pas trop de clients connectes
		cout << "liste : " << descripteursCV.size() << " clients : " << nbClientsMax << endl;
		if((int)descripteursCV.size() < nbClientsMax)
		{
			// Protege la liste pour ne pas perdre de donnees
			pthread_mutex_lock(&mutexDescripteurs);
		
			descripteursCV.push_back(descripteurCV);
		
			int taille = descripteursCV.size();
		
			// Envoie en permanence la grille actualisee au client
			pthread_create(&tabThreadsEnvoi[taille], NULL, threadClientEnvoi, &descripteursCV.back());
	
			// Recoit les demandes du client et gere sa deconnexion
			pthread_create(&tabThreadsReception[taille], NULL, threadClientReception, &descripteursCV.back());
		
			pthread_mutex_unlock(&mutexDescripteurs);
		}
		else
		{
			cout << "Demande de connexion refusee, trop de clients." << endl;
			close(descripteurCV);
		}
	}		
	
	return 0;
}

// Gere l'envoi de la grille perpetuellement au client (jusqu'a deconnexion)
void* threadClientEnvoi(void *arg)
{	
	int* descripteur = (int*)arg;
	cout << "Client connecté : " << *descripteur << endl;
	while(1)
	{
		cout << "Envoi de la grille au client : " << *descripteur << endl;
		int w;
		
		// On regarde si le client est en attente du controle sur la grille
		pthread_mutex_lock(&mutexAttente);
		
		list<int>::iterator it = attenteControle.begin();
		int attente = 0;
		while(it != attenteControle.end() && attente == 0)
		{
			if(*it == *descripteur)
				attente = 1;
			else
				++it;
		}
		
		pthread_mutex_unlock(&mutexAttente);	
		if(attente == 1)
		{
			// On signale au client qu'il est en attente
			char* envoi = preparationEnvoi(grille, 'a'); 
			
			w = write(*descripteur, envoi, strlen(envoi));
		}
		else
		{
			// On regarde si ce client possede le controle sur la grille
			if(*descripteur == controle)
			{
				// Indique au client qu'il a le controle
				char* envoi = preparationEnvoi(grille, 'c');
				
				w = write(*descripteur, envoi, strlen(envoi));
			}
			else
				w = write(*descripteur, grille, sizeof(grille));
		}
		if (w < 0)
		{
			// Le client s'est deconnecte
			close(*descripteur);
			return NULL;
		}
		sleep(1);
	}
}

// Gere la reception des commandes du client, gere aussi la deconnexion
void* threadClientReception(void *arg)
{
	int* descripteur = (int*)arg;
	while(1)
	{
		char commande[2];
		int r = recv(*descripteur, &commande, sizeof(commande), 0);
		if (r < 0)
		{
			perror("Echec : Reception impossible ");
		}

		cout << "Message recu de "<< *descripteur << ", controle = " << controle << endl;		
				
		if (commande[0] == 'q')
		{
			// Si ce client avait le controle
			if(controle == *descripteur)
			{
				// On le relache
				controle = -1;
				pthread_mutex_unlock(&mutexGrille);
				
				// Voir si il y avait un client en attente
				int client = majAttenteClients();
				
				if(client > -1)
					compteur = nbMouvementsMax;
			}
			
			// Si ce client etait en attente du controle
			pthread_mutex_lock(&mutexAttente);
			
			list<int>::iterator it = attenteControle.begin();
			int trouve = 0;
			while(it != attenteControle.end() && trouve == 0)
			{
				 if(*it == *descripteur)
				 	trouve = 1;
				 else
				 	++it;
			}
			
			if(trouve == 1)
				attenteControle.erase(it);
			pthread_mutex_unlock(&mutexAttente);
				
			// Fermeture
			cout << "Un client se deconnecte : " << *descripteur << endl;
			close(*descripteur);
			
			// Suppression du client dans la liste des descipteurs
			pthread_mutex_lock(&mutexDescripteurs);
			descripteursCV.remove(*descripteur);
			pthread_mutex_unlock(&mutexDescripteurs);
			
			return NULL;
		}
		
		// Si il a deja le controle sur la grille
		if (commande[0] == 'h' || commande[0] == 'b' || 
		    commande[0] == 'g' || commande[0] == 'd')
		{
			// Si c'est lui qui a le controle
			if (controle == *descripteur)
			{
				compteur--;
				cout << "Commande : " << commande[0] << endl;
				ecritureGrille(commande[0]);
				cout << "Il reste " << compteur << " mouvements au client." << endl;
				
				// Si c'est son dernier mouvement
				if(compteur == 0)
				{
					controle = -1;
					cout << "Perte de controle de : " << *descripteur << endl;
					pthread_mutex_unlock(&mutexGrille);
					
					int client = majAttenteClients();
					
					// Si un client a eu le controle 
					if(client > -1)
						compteur = nbMouvementsMax;
				}
			}
		}
			
		// Demande de controle du client		
		if(commande[0] == 'p')
		{
			cout << "Un client veut prendre le controle : " << *descripteur << endl;
			
			demandeControle(*descripteur);
			
			// Si le client a eu le controle 
			if(*descripteur == controle)
				compteur = nbMouvementsMax;
		}
	}
	
	return NULL;
}

// Gestion des commandes cote serveur pour terminer proprement
void* threadCommandes(void *arg)
{
	int *descripteurServeur = (int*)arg;
	while(1)
	{
		char buffer[3];
		
		fgets(buffer, sizeof(buffer), stdin);
	
		// Terminaison du serveur
		if(buffer[0] == 'q')
		{
			cout << "Arret du serveur." << endl;
			
			// Fermeture des sockets clients
			list<int>::iterator it;
			for(it=descripteursCV.begin(); it != descripteursCV.end(); ++it)
				close(*it);
			
			// Fermeture de la socket publique du serveur
			shutdown(*descripteurServeur, SHUT_RDWR);
			
			// Arret du programme
			exit(0);
		}
	}
}

// Manuel du serveur
void usage(FILE *stream)
{
	fprintf(stream, "Usage : \n");
	fprintf(stream, "serveur [port] optionnels : [nombreMouvementsMaximum] [nombreClientsMaximum] [historiqueGrille]\n");
}

// Gestion des ecritures dans la grille
void ecritureGrille(char commande)
{
	int position = 0;
	int nouvellePosition = -1;
	
	// Recherche de la position courante du curseur
	for(int i=0; i<16; i++)
	{
		if(grille[i] == '1')
			position = i;
	}

	if (commande == 'h' && position -4 >= 0)
	{
		cout << "Mouvement vers le haut" << endl;
		nouvellePosition = position -4;
		
	}
	else if (commande == 'b' && position + 4 < 16)
	{
		cout << "Mouvement vers le bas" << endl;
		nouvellePosition = position + 4;
	}
	else if (commande == 'g' && position-1 >= 0 && position-1 != 3 && position-1 != 7
				 && position-1 != 11)
	{
		cout << "Mouvement vers la gauche" << endl;
		nouvellePosition = position-1;
	}
	else if (commande == 'd' && position+1 != 4 && position+1 != 8 && position+1 != 12
				 && position+1 < 16)
	{
		cout << "Mouvement vers la droite" << endl;
		nouvellePosition = position+1;
	}
	
	if (nouvellePosition != -1)
	{
		grille[position] = '*';
		grille[nouvellePosition] = '1';
		compteurGrille--; // Decremente le nombre de choses a stocker dans l'historique de la grille
	}
	else
	{
		// Sinon afficher un message d'erreur
		cout << "Le client " << controle << " essaye de faire un mouvement impossible." << endl;
	}
	
	// Si l'on doit remettre la grille a zero
	if(compteurGrille == 0)
	{
		char nouvelleGrille[17] = "1000000000000000";
		
		// Recopie dans l'ancienne grille
		strcpy(grille, nouvelleGrille);
		
		cout << "Reinitialisation de l'historique de la grille du serveur." << endl;
		compteurGrille = historiqueGrille; // Reprends la valeur par defaut
	}
}

// Mets a jour la liste des clients en attente
// Donne eventuellement le controle au client attendant
void demandeControle(int client)
{
	pthread_mutex_lock(&mutexAttente);
			
	// Si personne n'a le controle
	if(controle == -1)			
	{		
		// Si personne n'attends
		if(attenteControle.empty())				
		{
			cout << "Prise de controle de la grille par le client : " << client << endl;			
				
			// Le client a desormais le controle sur la grille
			controle = client;
		}
		else
		{
			// Prendre le premier de la liste
			controle = attenteControle.front();
			
			cout << "Prise de controle de la grille par le client : " << controle << endl;
			
			attenteControle.pop_front();
			
			// Mettre en attente le client ayant fait la demande
			cout << "Mise en attente du client pour le controle de la grille : " << client << endl;
			
			attenteControle.push_back(client);			
		}
	}
	else
	{
		// Ajouter ce client a la liste
		attenteControle.push_back(client);
		
		cout << "Mise en attente du client pour le controle de la grille : " << client << endl;
	}
	
	pthread_mutex_unlock(&mutexAttente);
}

// Mets a jour la liste des clients en attente en donnant eventuellement
// le controle a l'un d'entre eux (si il y en a)
// Retourne -1 si la liste est vide, le descripteur du client ayant le controle sinon
int majAttenteClients()
{
	pthread_mutex_lock(&mutexAttente);
	
	if(attenteControle.empty())
	{
		pthread_mutex_unlock(&mutexAttente);
		return -1;
	}
	else
	{
		// Attribue le controle au premier de la liste
		controle = attenteControle.front();
		
		attenteControle.pop_front();
		
		cout << "Prise de controle de la grille par le client : " << controle << endl;
	
		pthread_mutex_unlock(&mutexAttente);
		
		return controle;
	}
}

// Alloue et prepare la chaine avec un caractere de controle
char* preparationEnvoi(char g[17], char carControle)
{
	// Allocation de la nouvelle grille
 	char *envoi = (char*)malloc(18*sizeof(char));
	
	// Recopie de l'ancienne grille
	strncpy(envoi, grille, 16); 
	
	// Formatage de la chaine et ajout du caractere de controle
	envoi[16] = carControle;
	envoi[17] = '\0';

	return envoi;
}


